package spectrum;


import ikelib.Complex;
import ikelib.FFT;
import ikelib.Field;
import ikelib.drawTools;
import ikelib.wintime;

import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Stroke;
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferedImage;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;

import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.filechooser.FileNameExtensionFilter;

import wcg.ChannelConfig;
import wcg.ChannelDataDisplayProperty;
import wcg.winDataBase;
import wcg.winchkgProperty;




public class SpectrumView extends JPanel implements Printable,MouseListener,MouseMotionListener,DropTargetListener {
	
	private boolean XScalePeriod = false;
	public  static final int COLOR_COLOR_WHITE_BACK = 0;
	public  static final int COLOR_COLOR_BLACK_BACK = 1;
	public  static final int COLOR_BW_WHITE_BACK = 2;
	public  static final int COLOR_BW_BLACK_BACK = 3;
	public  static final int COLOR_AUTO_BY_CHANNEL = 0;
	public  static final int COLOR_AUTO_BY_STATION = 1;
	public  static final int COLOR_AUTO_BY_COMPONENT=2;
	private Color ColorBackGround = Color.white;  //  @jve:decl-index=0:
	private Color ColorAxis = Color.black;
	private Color ColorAxisSub = Color.gray;
	private Color ColorAxisLabel = Color.gray;
	private Color ColorTextInfo = Color.gray;
	private Color ColorCurMarker = Color.orange;
	private Color ColorMarker = Color.blue;
	private int ColorMode = COLOR_COLOR_WHITE_BACK;
	/////////////////////////////////////
	private boolean YLogScale = true;
	private String YAxisLabel = "[dB]";
	private boolean IsPrint = false;
	private winDataBase DataBase=null;  //  @jve:decl-index=0:
	//private Field [] FFTedData;
	// -------------------------------------------
	private int CurChannel = 0;
	private int CurFFTedSampleRate=1;
	private int RefChannel = -1;
//	private Complex [] Data=null;
//	private double [] FFTYaxis;
//	private double [] FFTedFreq;
//	private double FFTedAmplitudeMax ;
//	private double FFTedAmplitudeMin;
//	private double FFTedFreqMin;
//	private double FFTedFreqMax;
	private int [] DispDataX = null;
	private int [] DispDataY = null;
	
	private TreeMap<Integer,FFTChData> DataSet = new TreeMap<Integer,FFTChData>();
	
	//private Complex [] FFTedData;
	private int OverlapCount=1;
	private boolean Relative = true;
	private double DisplayAmplitudeMax = 20;
	private double DisplayAmplitudeMin = -180;
	private double DisplayFreqMax=1000;
	private double DisplayFreqMin=0.00001;
	private double FullScale=0;
	private boolean XLogScale = true;
	private int 	WindowFunction = 1;
	public static final int WINDOW_FUNCTION_RECTANGULAR = 0;
	public static final int WINDOW_FUNCTION_HAN = 1;
	public static final int WINDOW_FUNCTION_HAMMING = 2;
	public static final int WINDOW_FUNCTION_BLACKMAM = 3;
	private int FFTlength = 4096;
	private int FFTlengthOld = 0;
	private int FFTlengthAuto = 4096;
	
	private boolean OverLapping = false;
	private boolean AutoAmpScale = false;
	private boolean DCOffsetCancel=true;

	public final int CALC_MODE_DBFSPOWER = 0;
	public final int CALC_MODE_DBFSAMP = 1;
	private int  	CalcMode = CALC_MODE_DBFSPOWER;	// 0: dBFS power , 1:dBFS amp
	
	private ChannelConfig ChConfig = null;
	private static final String INIT_FILE = "Channel.ini";
	private Point MousePosition = new Point();
	private boolean MouseActive=false;
	private static final int MARKER_MAX = 7;
	private int[] MarkerIndex = new int[MARKER_MAX];
	private Rectangle Rgraph=new Rectangle();
	private Rectangle Rall=new Rectangle();
	private boolean MarkerAutoPeak = false;
	private boolean FFTBusy = false;
	
	JPopupMenu PopupMenuSpectrum = new JPopupMenu(); // @
	private int FFTCalcCounter=0;
	private long PaintTimeStart;
	private long PaintTimeEnd;
	private long CalcTimeStart;
	private long CalcTimeEnd;
	private boolean OffsetDisplay=false;
	private boolean SpectrumDensity = false;

	
	/**
	 * まとめるだけのクラス
	 */
	private class FFTChData{
		//public int Channel;
		public Complex[] Data;
		public int FFTedSampleRate;
		public double [] FFTYaxis;
		public double [] FFTedFreq;
		public double FFTedAmplitudeMax ;
		public double FFTedAmplitudeMin;
		public double FFTedFreqMin;
		public double FFTedFreqMax;
		FFTChData(){
		//	Channel = 0;
		//	Data = null;
			FFTedSampleRate = 1;
			FFTYaxis = null;
			FFTedFreq = null;
			FFTedAmplitudeMax = -Double.MAX_VALUE;//Double.MIN_VALUE;
			FFTedAmplitudeMin = Double.MAX_VALUE;
			FFTedFreqMin = Double.MAX_VALUE; ;
			FFTedFreqMax = -Double.MAX_VALUE;//Double.MIN_VALUE;
		}
		
	}
	public void setRelative(boolean b){
		Relative = b;
		AutoAmpScale=true;
		doFFT();
	}
	/**
	 * 
	 */
	public void setMode(int mode){
		CalcMode = mode;
		AutoAmpScale=true;
		doFFT();
	}
	// -------------------------
	// png にエクスポート
	public String ExportImageDir = null;
	
	public boolean exportAsImage(String dir,double ratex,double ratey){
		Dimension dm = this.getSize(); 

		BufferedImage img = new BufferedImage((int)(dm.width*ratex), (int)(dm.height*ratey), BufferedImage.TYPE_INT_RGB);
		Graphics2D g = img.createGraphics();
		g.scale(ratex,ratey);
		IsPrint = true;
		paintComponent(g);
		IsPrint = false;
		
		// ファイルネームを作る
//		String filename = dir + "Spectrum";
//		synchronized(DataSet){
//			Set<Integer> set = DataSet.keySet();
//			Iterator<Integer> ite = set.iterator();
//			int counter=0;
//			while(ite.hasNext()){
//				int ch = ite.next();
//				filename += String.format("-%04x", ch);
//				if(counter++ > 6) break;
//			}
//		}
//		filename += ".png";
		
		String filename = dir + "Spectrum-" + DataTimeStart.fmt2()+"-"+DataTimeEnd.fmt2()+".png";

		
		
		//------------------------------------------------------
		// save as ダイアログ
		JFileChooser filechooser = new JFileChooser(dir);//@
		filechooser.setFileSelectionMode( JFileChooser.FILES_ONLY);	      
		FileNameExtensionFilter filter = new FileNameExtensionFilter("png file", "png");
		filechooser.addChoosableFileFilter(filter);
		filechooser.setDialogTitle("Save as");
		File tmp = new File(dir + filename);
		filechooser.setSelectedFile(tmp);
		//filechooser.ensureFileIsVisible(tmp);
		int selected = filechooser.showSaveDialog(null);
		if ( selected == JFileChooser.APPROVE_OPTION ) {
			tmp = filechooser.getSelectedFile();
			if(tmp.exists()){
				if(JOptionPane.NO_OPTION ==  JOptionPane.showConfirmDialog(null, "Over write ?","Confirmation",JOptionPane.YES_NO_OPTION)){
					return false;
				}
			}
			filename = tmp.getAbsolutePath();
			ExportImageDir = new String(tmp.getParentFile().getAbsolutePath()+File.separator);
			
		}
		else{
			return false;
		}
		//------------------------------------------------------
		
		try {
			ImageIO.write(img, "png", new File(filename));
		} catch (IOException e) {
			e.printStackTrace();
			JOptionPane.showMessageDialog(null, "File Output Error", "Error", JOptionPane.ERROR_MESSAGE); 
			return false;
		}
		return true;
	}
	
	public void setWinDataBase(winDataBase d){
		DataBase = d;
	}
	//------------------------------------------------------
	// 
	public boolean getSpectrumDensity(){
		return SpectrumDensity;
	}
	public void setSpectrumDensity(boolean enable){
		SpectrumDensity = enable;
		doFFT();
	}
	//------------------------------------------------------
	// 
	public double getFullScall(){
		return FullScale;
	}
	//------------------------------------------------------
	// 
	public void setAutoScale(){
		AutoAmpScale=true;
		autoScaleAmplitude();
		AutoAmpScale=false;
		repaintAll();
	}
	//------------------------------------------------------
	// 
	public boolean isBusy(){
		return FFTBusy;
	}
	//------------------------------------------------------
	// 
	public void  clearAllMarker(){
		for(int i=0;i<MarkerIndex.length;i++){
			MarkerIndex[i] = -1;
		}
	}
	private FFTChData getRefChData(){
		FFTChData ret;
		BusyPaint=true;
		synchronized(DataSet){
			//System.out.println("getRefChData");
			ret =  DataSet.get(RefChannel);
			BusyPaint=false;
		}
		return ret;
	}
	public double getMarkerFreq(int marker){
		//System.out.println("getMarkerFreq");
		FFTChData cd = getRefChData();
		if(cd == null) return 0.0;
		double[] fFTedFreq = cd.FFTedFreq;
		if(marker>=MarkerIndex.length) return 0.0;
		int idx = MarkerIndex[marker];
		if(idx<0) return 0.0;
		if(fFTedFreq.length <= idx) return 0.0;
		return fFTedFreq[idx];
	}
	public double getMarkerLevel(int marker){
		//System.out.println("getMarkerLevel");
		FFTChData cd = getRefChData();
		if(cd == null) return 0.0;
		double[] fFTYaxis = cd.FFTYaxis;
		if(marker>=MarkerIndex.length) return 0.0;
		int idx = MarkerIndex[marker];
		if(idx<0) return 0.0;
		if(fFTYaxis.length <= idx) return 0.0;
		return fFTYaxis[idx];
	}
	//------------------------------------------------------
	// 
	public boolean isDCOffsetCancel() {
		return DCOffsetCancel;
	}
	public void setDCOffsetCancel(boolean dCOffsetCancel) {
		DCOffsetCancel = dCOffsetCancel;
		doFFT();
	}
	private static final long serialVersionUID = 1L;
	public SpectrumViewPanel parent=null;
	
	//------------------------------------------------------
	// 
	SpectrumView(){
		FullScale=1.0*Math.pow(2.0,23.0);
		ChConfig = new ChannelConfig(INIT_FILE);
		ChConfig.load(INIT_FILE);
		MousePosition.x = MousePosition.y = -1; 
		addMouseListener(this);
		addMouseMotionListener(this);
		for(int i=0;i<MarkerIndex.length;i++){
			MarkerIndex[i] = -1;
		}
		// ---------------------------------------------------------------
		// ポップアップメニュー
		// ----------------------
		// すべてのマーカーをクリア
		addPopupMenuItem(PopupMenuSpectrum,"Clear all marker", new ActionListener(){ 
			public void actionPerformed(ActionEvent e) {
				for(int i=1;i<MarkerIndex.length;i++){
					MarkerIndex[i]=-1;
				}
			}
	    });
		addPopupMenuItem(PopupMenuSpectrum,"Clear Refference marker", new ActionListener(){ 
			public void actionPerformed(ActionEvent e) {
				MarkerIndex[MarkerIndex.length-1] = -1;
			}
	    });
		for(int i=1;i<MarkerIndex.length-1;i++){
			String mes = String.format("Clear marker %d", i);
			addPopupMenuItem(PopupMenuSpectrum,mes, new ActionListener(){ 
				public void actionPerformed(ActionEvent e) {
					String para = e.getActionCommand();
					char c  = para.charAt(para.length()-1);
					int id = c - '0';
					if(id>=1 && id<MarkerIndex.length)	MarkerIndex[id] = -1;
				}
		    });
		}
		PopupMenuSpectrum.addSeparator();
		addPopupMenuItem(PopupMenuSpectrum,"Copy marker value to clipboard", new ActionListener(){ 
			public void actionPerformed(ActionEvent e) {
				String mes = new String();
//				boolean delta = false;
//				if(MarkerIndex[MarkerIndex.length-1] >= 0) {
//					delta = true;
//					mes += "R \t";
//					mes += String.format(" %8.3f \tdB\t %8.3f \tHz\t\n",FFTYaxis[MarkerIndex[MarkerIndex.length-1]],FFTedFreq[MarkerIndex[MarkerIndex.length-1]]);
//				}
//				for(int i=0;i<MarkerIndex.length-1;i++){
//					if(MarkerIndex[i]!=-1){
//						if(i==0){
//							if(delta)	mes += "Δ@ \t";
//							else 			mes += "@ \t";
//						} else if(i==MarkerIndex.length-1){
//							mes += "R \t";
//						}
//						else {
//							if(delta) 	mes += String.format("Δ%d \t", i);
//							else 		mes += String.format("%d \t", i);
//						}
//						if(delta){
//							mes += String.format(" %8.3f \tdB\t %8.3f \tHz\t\n",FFTYaxis[MarkerIndex[i]]-FFTYaxis[MarkerIndex[MarkerIndex.length-1]],FFTedFreq[MarkerIndex[i]]-FFTedFreq[MarkerIndex[MarkerIndex.length-1]]);
//						}
//						else{
//							mes += String.format(" %8.3f \tdB\t %8.3f \tHz\t\n",FFTYaxis[MarkerIndex[i]],FFTedFreq[MarkerIndex[i]]);
//						}
//					}
//				}
				String[] tk = getMarkerStrings();
				for(int i=0;i<tk.length;i++){
					String m = tk[i];
					if(m==null){
						continue;
					}
					mes += m;
					mes += "\n";
				}
				Toolkit kit = Toolkit.getDefaultToolkit();
				Clipboard clip = kit.getSystemClipboard();
				StringSelection ss = new StringSelection(mes);
				clip.setContents(ss, ss);
			}
	    });
		addPopupMenuItem(PopupMenuSpectrum,"Attach cursor to...", new ActionListener(){ 
			public void actionPerformed(ActionEvent e) {
				String ini = String.format("%04x", RefChannel);
				String ret = JOptionPane.showInputDialog(null,(Object)"Input channel ID",(Object)ini);
				if(ret == null) {
					return;
				}
				try{
					int ch = Integer.parseInt(ret, 16);
					BusyPaint=true;
					synchronized(DataSet){
						//System.out.println("Attach coursor");

						Set<Integer> s = DataSet.keySet();
						Iterator<Integer> ite = s.iterator();
						while(ite.hasNext()){
							if(ite.next() == ch){
								RefChannel = ch;
							}
						}
					}
					BusyPaint=false;
				}
				catch(NumberFormatException ee){
					//ee.printStackTrace();
				}
			}
	    });

		new DropTarget(this,this);
		
	}
	// ------------------------------
	// ポップアップメニューをＡＤＤする
	private JMenuItem addPopupMenuItem(JPopupMenu popup,String name, ActionListener al) {
		JMenuItem item = new JMenuItem(name); // @
		item.addActionListener(al);
		popup.add(item);
		return item;
	}

	//------------------------------------------------------
	// 
	public int getDataLength(){
		FFTChData cd = getRefChData();
		if(cd == null) return 0;
		if(cd.Data==null) return 0;
		return cd.Data.length;
	}
	//------------------------------------------------------
	// 
	public int getFFTSamplerate(){
		FFTChData cd = getRefChData();
		if(cd == null) return 1;
		return getRefChData().FFTedSampleRate; 
	}
	//------------------------------------------------------
	// 
	public int getOverlapCount(){
		return OverlapCount;
	}
	//------------------------------------------------------
	// 
	public float getFreqResolution(){
		float ret = (float)getFFTSamplerate() / (float)getFFTlength();
		return ret;
	}
	//------------------------------------------------------
	// 
	public boolean isOverLapping() {
		return OverLapping;
	}

	//------------------------------------------------------
	// 
	public void setOverLapping(boolean overLapping) {
		OverLapping = overLapping;
		doFFT();
	}

	// ------------------------------------------
	public int getFFTlength() {
		if(FFTlength==0) return FFTlengthAuto;
		return FFTlength;
	}

	//------------------------------------------------------
	// 
	public void setFFTlength(int fFTlength) {
		FFTlength = fFTlength;
		doFFT();
	}
	
	// ------------------------------------------
	public int getWindowFunction() {
		return WindowFunction;
	}

	//------------------------------------------------------
	// 
	public void setWindowFunction(int windowFunction) {
		WindowFunction = windowFunction;
		doFFT();
	}

	// ------------------------------------------
	public void setXLogScale(boolean b){
		XLogScale = b;
		repaintAll();
	}
	//------------------------------------------------------
	// 
	public boolean getXLogScale(){
		return XLogScale;
	}
	//------------------------------------------------------
	// 
	public void resetDisplayFreq(){
		AutoAmpScale=true;
		autoScaleAmplitude();
		AutoAmpScale=false;
		repaintAll();
	}
	//------------------------------------------------------
	// 
	public void setDisplayFreqMax(double max){
		if(DisplayFreqMin >= max) return;
		DisplayFreqMax=max;
		repaintAll();
	}
	//------------------------------------------------------
	// 
	public double getDisplayFreqMax(){
		return DisplayFreqMax;
	}
	//------------------------------------------------------
	// 
	public void setDisplayFreqMinUp(){
		if(XLogScale){
			if(DisplayFreqMax >  DisplayFreqMin*10) {
				DisplayFreqMin *= 10;
				//if(DisplayFreqMin<0.0001)DisplayFreqMin=0.0001; 
				repaintAll();
			}
		}
		else{
			double range=getFFTSamplerate()/2;
			if(DisplayFreqMax > DisplayFreqMin+range/10){
				DisplayFreqMin += range/10; 
				repaintAll();
			}
		}
	}
	//------------------------------------------------------
	// 
	public void setDisplayFreqMinDown(){
		if(XLogScale){
			if(1e-8 <  DisplayFreqMin) { // min Exp(-8)まで表示するが、データがないとだめよ
				DisplayFreqMin /= 10;
				repaintAll();
			}
		}
		else{
			double range=getFFTSamplerate()/2;
			DisplayFreqMin -= range/10;
			if(DisplayFreqMin<=0.0) DisplayFreqMin = 1e-8;
			repaintAll();
		}
	}
	//------------------------------------------------------
	// 
	public void setDisplayFreqMaxUp(){
		if(XLogScale){
			DisplayFreqMax *= 10;
			if(DisplayFreqMax > getFFTSamplerate()*10) DisplayFreqMax = getFFTSamplerate()*10;
			repaintAll();
		}
		else{
			double range=getFFTSamplerate()/2;
			DisplayFreqMax += range/10;
			if(DisplayFreqMax > getFFTSamplerate()/2) DisplayFreqMax = getFFTSamplerate()/2;
			repaintAll();
		}
		
	}
	//------------------------------------------------------
	// 
	public void setDisplayFreqMaxDown(){
		if(XLogScale){
			DisplayFreqMax /= 10;
			if(DisplayFreqMax < DisplayFreqMin*10) DisplayFreqMax = DisplayFreqMin*10;
			repaintAll();
		}
		else{
			double range=getFFTSamplerate()/2;
			if(DisplayFreqMax > DisplayFreqMin+range/10){
				DisplayFreqMax -= range/10;
				repaintAll();
			}
		}
	}
	//------------------------------------------------------
	// 
	public void setDisplayFreqMin(double min){
		if(DisplayFreqMax <= min) return;
		DisplayFreqMin=min;
		repaintAll();
	}
	//------------------------------------------------------
	// 
	public double getDisplayFreqMin(){
		return DisplayFreqMin;
	}
	//------------------------------------------------------
	// 
	public void setDisplayAmplitudeMax(double max){
		double delta = max - DisplayAmplitudeMax;
		DisplayAmplitudeMax += delta;
		DisplayAmplitudeMin += delta;
		repaintAll();
	}
	//------------------------------------------------------
	// 
	public double getDisplayAmplitudeMax(){
		return DisplayAmplitudeMax;
	}
	public double getDisplayAmplitudeMin(){
		return DisplayAmplitudeMin;
	}
	//------------------------------------------------------
	// 
	public void setDisplayAmplitudeStep(double step){
		DisplayAmplitudeMin=DisplayAmplitudeMax-step*10.0;
		repaintAll();
	}
	//------------------------------------------------------
	// 
	public double getDisplayAmplitudeStep(){
		return (DisplayAmplitudeMax-DisplayAmplitudeMin)/10.0;
	}
	// ------------------------------------------
	private void autoScaleAmplitude(){
		
		double fFTedAmplitudeMax = -Double.MAX_VALUE;//Double.MIN_VALUE;
		double fFTedAmplitudeMin = Double.MAX_VALUE;
		double fFTedFreqMin = Double.MAX_VALUE;
		double fFTedFreqMax = -Double.MAX_VALUE;//Double.MIN_VALUE;
		double[] freqMin =null;;
		double[] freqMax =null;
		
		int index = 0;
		BusyPaint=true;
		synchronized(DataSet){
			//System.out.println("autoScaleAmp");

			Set<Integer> chset = DataSet.keySet();
			Iterator<Integer> ite = chset.iterator();
			while(ite.hasNext()){
				int ch = ite.next();
				FFTChData cd =  DataSet.get(ch);
				if(fFTedAmplitudeMax < cd.FFTedAmplitudeMax)  fFTedAmplitudeMax = cd.FFTedAmplitudeMax;
				if(fFTedAmplitudeMin > cd.FFTedAmplitudeMin)  fFTedAmplitudeMin = cd.FFTedAmplitudeMin;
				if(fFTedFreqMax < cd.FFTedFreqMax){
					fFTedFreqMax = cd.FFTedFreqMax;
					freqMax = cd.FFTedFreq;
				}
				if(fFTedFreqMin > cd.FFTedFreqMin){
					fFTedFreqMin = cd.FFTedFreqMin;
					freqMin = cd.FFTedFreq;
				}
				index++;
			}
		}
		BusyPaint=false;
		if(freqMin==null || freqMax==null){
			return;
		}
		if(AutoAmpScale){
			if(YLogScale){
				DisplayAmplitudeMax = (Math.ceil(fFTedAmplitudeMax / 10)) * 10.0;
				if(DisplayAmplitudeMax - 1*10 		< fFTedAmplitudeMin)  DisplayAmplitudeMin = DisplayAmplitudeMax - 1*10;
				else if(DisplayAmplitudeMax - 2*10 	< fFTedAmplitudeMin)  DisplayAmplitudeMin = DisplayAmplitudeMax - 2*10;
				else if(DisplayAmplitudeMax - 5*10 	< fFTedAmplitudeMin)  DisplayAmplitudeMin = DisplayAmplitudeMax - 5*10;
				else if(DisplayAmplitudeMax - 10*10 < fFTedAmplitudeMin)  DisplayAmplitudeMin = DisplayAmplitudeMax - 10*10;
				else if(DisplayAmplitudeMax - 20*10 < fFTedAmplitudeMin)  DisplayAmplitudeMin = DisplayAmplitudeMax - 20*10;
				else   DisplayAmplitudeMin = DisplayAmplitudeMax - 50*10;
			}
			else{
				DisplayAmplitudeMax = fFTedAmplitudeMax;
				DisplayAmplitudeMin = fFTedAmplitudeMin;
			}
			if(XLogScale){
				double xmin = fFTedFreqMin;
				double xmax = fFTedFreqMax;
				if(fFTedFreqMin<freqMin[4]){
					xmin = freqMin[4];
				}
				if(fFTedFreqMax>freqMax[freqMax.length-1]){
					xmax = freqMax[freqMax.length-1];
				}
				DisplayFreqMin = roundFloor(xmin);
				DisplayFreqMax = roundCeil(xmax);
			}
			else {
				DisplayFreqMin = fFTedFreqMin;
				DisplayFreqMax = fFTedFreqMax;
			}
		}
	}
	
	// ------------------------------------------
	// Window Function
	private void windowFunction(Field[] f){
		int len = f.length;
		double sum = 0.0;
		double [] win = new double[len];

		switch(WindowFunction){
		case WINDOW_FUNCTION_HAN:
			// 0.5 - 0.5cos(2pn/N-1)
			for(int i=0;i<len;i++){
				win[i] = 0.5-0.5*Math.cos(2*Math.PI*(double)i/(double)(len-1));
				sum += win[i];
			}
			break;
		case WINDOW_FUNCTION_HAMMING:
			// 0.54 - 0.46cos(2pn/N-1)
			for(int i=0;i<len;i++){
				win[i] = 0.54-0.46*Math.cos(2*Math.PI*(double)i/(double)(len-1));
				sum += win[i];
			}
			break;
		case WINDOW_FUNCTION_BLACKMAM:
			// 0.42 - 0.5cos(2pn/N-1)+0.08cos(4pn/N-1)
			for(int i=0;i<len;i++){
				win[i] = 0.42-0.5*Math.cos(2*Math.PI*(double)i/(double)(len-1))+0.08*Math.cos(4*Math.PI*(double)i/(double)(len-1));
				sum += win[i];
			}
			break;
		case WINDOW_FUNCTION_RECTANGULAR:
		default:	
			return;
		}
		
		for(int i=0;i<len;i++){
			((Complex)f[i]).multi_real(win[i]*len/(double)sum);
		}
		return;
	}

	/*
	 *  FFT計算を行う
	 */
	private Complex[] singleFFT(Complex[] data,int index ,int fftlen){
	
		Complex[] fFTedData = new Complex[fftlen];
		int max = data.length;
		// データ長が足りなければ０を挿入する
		for(int i=0;i<fftlen;i++){
			if((i+index) < max) 	fFTedData[i] = (Complex) data[i+index].copy();
			else 					fFTedData[i] = new Complex(0.0,0.0);
		}
		//windowFunction(fFTedData);
		// DCオフセットキャンセル
		if(DCOffsetCancel){
			// ----------------------------------------------------
			// Canceling DC offset
			Complex sum=new Complex(0.0,0.0); // Cancel DC Offset
			for(int i=0;i<fftlen;i++){
				if(i+index<max) 	sum.addTo(fFTedData[i].copy());
			}
			sum.div(fftlen);
			sum.negate();
			for(int i=0;i<fftlen;i++){
				fFTedData[i].addTo(sum);
			}
		}
		// 窓関数をかける
		windowFunction(fFTedData);
		// ----------------------------------------------------
		// FFT
		FFT.execute(false, fFTedData);
		FFTCalcCounter++;
		return fFTedData;
	}
	
	// ------------------------------------------
	// 複素フーリエ係数から振幅を求める
	private double[] getAmplitude(Complex[] f){
		int point = f.length;
		double[] ret = new double[point/2+1];
		for(int i=0;i<point/2+1;i++){
			double ak = f[i].real()/Math.sqrt(2.0);
			double bk = f[i].imag()/Math.sqrt(2.0);
			ret[i] = 2*Math.sqrt(ak*ak+bk*bk);
		}
		return ret;
	}
	// ------------------------------------------
	// 複素フーリエ係数からパワースペクトルを求める
	private double[] getPowerSpectrum(Complex[] f){
		int point = f.length;
		double[] ret = new double[point/2+1];
		for(int i=0;i<point/2+1;i++){
			double ak = f[i].real()/Math.sqrt(2.0);
			double bk = f[i].imag()/Math.sqrt(2.0);
			if(i==0 || i == point/2){
				ret[i] =   (ak*ak+bk*bk);
			}
			else{
				ret[i] = 2*(ak*ak+bk*bk);	
			}
		}
		return ret;
	}
	// ------------------------------------------
	public  void ChannelDataReLoad(){
		ChConfig.load();
	}
	private ChannelDataDisplayProperty getChannelProperty(int channel){
		return ChConfig.getData(channel);
	}
	/**
	 * データをセットする
	 * @param d データ
	 * @param samplerate　サンプルレート
	 * @param ch　チャネル
	 * @return true:成功
	 */
	public boolean setData(int [] d,int samplerate,int ch){
		Complex[] data = new Complex[d.length];
		for(int i=0;i<d.length;i++){
			double re = (double)d[i];
			double im = 0.0;
			Complex c = new Complex(re,im);
			data[i] = c;
		}
		CurFFTedSampleRate=samplerate;
		CurChannel = ch;
		FFTChData cd = new FFTChData();
		cd.Data  = data;
		cd.FFTedSampleRate=samplerate;
		BusyPaint=true;
		synchronized(DataSet){
			DataSet.put(ch, cd);
		}
		BusyPaint=false;
		if(RefChannel==-1){
			setRefChannel(ch);
		}
		if(FFTBusy) return false;
		return doFFT();
	}
	// ------------------------------------------
	/**
	 * setDataされたDataSetを使って FFTを実行する
	 * @return
	 */
	private boolean doFFT(){
		BusyPaint=true;
		boolean ret = false;
		synchronized(DataSet){
			Set<Integer> chset=DataSet.keySet();
			Iterator<Integer> ite = chset.iterator();
			while(ite.hasNext()){
				int ch = ite.next();
				FFTChData cd = DataSet.get(ch);
				ret = doFFT(cd);
			}
		}
		BusyPaint=false;
		autoScaleAmplitude();
		repaintAll();
		return ret;
	}
	/**
	 * FFTChDataのＦＦＴを実行する。（ここが本体）
	 * @param cd
	 * @return
	 */
	private boolean doFFT(FFTChData cd){
		if(cd.Data==null) return false;
		FFTBusy = true;
		// データ長は512以上に限定
		int datalen = cd.Data.length;
		if(datalen < 16){
			FFTBusy=false;
			return false;
		}
		CalcTimeStart = System.currentTimeMillis();	
		setCursor((Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)));
		
		// ２のべき乗に丸める
		int fftlen = (int)Math.floor((Math.log(datalen)/Math.log(2.0)));
		fftlen = (int)Math.pow(2.0, fftlen);
		FFTlengthAuto = fftlen;
		if(FFTlength!=0){
			fftlen = FFTlength;
		}
		//parent.message(String.format("Data length=%d\n",datalen));
		//parent.message(String.format("FFT length=%d\n",fftlen));
		// ＦＦＴデータ長が変更されたらマーカーをクリア
		if(FFTlengthOld != fftlen) clearAllMarker();
		FFTlengthOld = fftlen;
		
		/////////////////////////////////////////////////////////////////////
		ChannelDataDisplayProperty pro = getChannelProperty(CurChannel);
		double factor = 1.0;
		//boolean unitConv = false;
		String unitStr = "digit";
		if(pro==null){
			FullScale = 8388608;
		}
		else{
			FullScale = pro.FullScale;
			if(pro.unitType==ChannelDataDisplayProperty.UNIT_TYPE_CONV){
				factor = pro.unitFactor;
				FullScale = FullScale*factor;
				//unitConv = true;
				unitStr = pro.unitString;
			}
		}
		/////////////////////////////////////////////////////////////////////
		// データを複素数配列にコピー
		Complex[] data; 
		if(fftlen<=datalen){
			data = new Complex[datalen];
			for(int i=0;i<datalen;i++){
				data[i] = (Complex) cd.Data[i].copy();
				data[i].multi_real(factor);
			}
		}
		// もしデータ長が短かったら０で埋める
		else{
			data = new Complex[fftlen];
			for(int i=0;i<fftlen;i++){
				if(i<datalen) data[i] = (Complex) cd.Data[i].copy();
				else 		  data[i] = new Complex(0.0,0.0);
			}
			datalen = data.length;
		}
		
		// データが長ければオーバーラッピングしながらＦＦＴを繰り返す
		HashSet<Complex[]> ts = new HashSet<Complex[]>();
		if(OverLapping){
			for(int i=0;i+fftlen<=datalen;i+=fftlen/2){
				Complex[] fd = singleFFT(data,i,fftlen);
				ts.add(fd);
			}
			OverlapCount = ts.size();
		}
		else{
			Complex[] fd = singleFFT(data,datalen-fftlen,fftlen);
			ts.add(fd);
			OverlapCount=1;
		}
		//parent.message(String.format("Overlapped=%d\n",ts.size()));
		// 表示データを計算する
		if(CalcMode == CALC_MODE_DBFSPOWER){
			cd.FFTYaxis = CalcPower(false,fftlen,ts,Relative,FullScale,cd.FFTedSampleRate,Relative);
			if(Relative){
				if(YLogScale){
					YAxisLabel = "[dBFs";
				}
				else{
					YAxisLabel = "[digit";
				}
			}
			else{
				YAxisLabel = "[" + "("+unitStr + ")^2";
			}
			if(SpectrumDensity) YAxisLabel += " / Hz]";
			else 			YAxisLabel += "]";
		}
		else if(CalcMode == CALC_MODE_DBFSAMP){
			cd.FFTYaxis = CalcPower(true,fftlen,ts,Relative,FullScale,cd.FFTedSampleRate,Relative);
			if(Relative){
				if(YLogScale){
					YAxisLabel = "[dBFs";
				}
				else{
					YAxisLabel = "[digit";
				}
			}
			else{
				YAxisLabel = "[" + unitStr ;
			}
			if(SpectrumDensity) YAxisLabel += " / rtHz]";
			else 			YAxisLabel += "]";
		}
		
		
		
		// 表示データのｗ最大値最小値
		double max = -Double.MAX_VALUE;//Double.MIN_VALUE;
		double min = Double.MAX_VALUE;
		for(int i=0;i<cd.FFTYaxis.length;i++){
			if(cd.FFTYaxis[i] == Double.NEGATIVE_INFINITY || cd.FFTYaxis[i] == Double.POSITIVE_INFINITY){
				continue;
			}
			if(max < cd.FFTYaxis[i]) max = cd.FFTYaxis[i];
			if(min > cd.FFTYaxis[i]) min = cd.FFTYaxis[i];
		}
		cd.FFTedAmplitudeMax = max;
		cd.FFTedAmplitudeMin = min;
//		if(min < -150) FFTedAmplitudeMin = -150;
//		else FFTedAmplitudeMin = min;
		// 周波数データを作成
		cd.FFTedFreq = new double [fftlen/2+1];
		double resolution = (double)cd.FFTedSampleRate/(fftlen);
		for(int i=0;i<fftlen/2+1;i++){
			cd.FFTedFreq[i] = (double)i*resolution;
		}
		cd.FFTedFreqMin = 0;
		cd.FFTedFreqMax = (double)CurFFTedSampleRate/2;
		

		//parent.message(String.format("Amplitude= \n  %4.1fdB,\n  %4.1fdB\n",FFTedAmplitudeMin,FFTedAmplitudeMax));
		//parent.message(String.format("Frequency=%4.1fHz\n",FFTedFreqMax));
		setCursor((Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR )));
		CalcTimeEnd = System.currentTimeMillis();	
		FFTBusy=false;
		AutoAmpScale=false;
		return  true;
	}
	/**
	 * 
	 * @param ch
	 * @return
	 */
	private Color getDataColor(int ch){
		ChannelDataDisplayProperty pro = getChannelProperty(ch);
		if(pro!=null){
			pro.setColorCalcMode(ColorCalcMode);
		}
		switch(ColorMode){
		case COLOR_COLOR_WHITE_BACK:
			if(pro==null){
				return Color.gray;
			}
			return pro.getDataColor();
		case COLOR_COLOR_BLACK_BACK:
			if(pro==null){
				return Color.white;
			}
			return pro.getDataColor();
		case COLOR_BW_BLACK_BACK:
			return Color.white;
		}
		return Color.gray;
	}
	/**
	 * FourierAmpで計算する
	 * @param fftlen
	 * @param ts
	 * @return
	 */
	private double[] CalcPower(boolean isAmp,int fftlen,HashSet<Complex[]> ts,boolean relative,double fullscale,int samplerate,boolean dB) {
		int overlap = ts.size();
	//　結果を振幅、周波数配列に入れる 
		boolean first=true;
		double[] ret = new double[fftlen/2+1];
		Iterator<Complex[]> ite = ts.iterator();
		while(ite.hasNext()){
			Complex[] fd = ite.next();
			double[] amp2;
			if(isAmp) 	amp2 = getAmplitude(fd); 		// 単位はV相当 スペクトルの振幅に相当
			else    	amp2 = getPowerSpectrum(fd);
			if(first){
				System.arraycopy(amp2, 0, ret, 0, ret.length);
				first = false;
			}
			else{
				for(int j=0;j<ret.length;j++){
					ret[j] += amp2[j];
				}
			}
		}
		double resolution = (double)samplerate / (double)fftlen;
		double fullrange;
		if(isAmp) 	fullrange = fullscale;
		else 		fullrange = fullscale * fullscale;
		for(int i=0;i<ret.length;i++){
			ret[i] /= (overlap);
			if(relative){
				ret[i] /= fullrange;
			}
		}
		
		if(SpectrumDensity){
			if(isAmp)	ret = CalcDensity(ret,resolution,1.0,true);
			else		ret = CalcDensity(ret,resolution,1.0,false);
		}
		for(int i=0;i<ret.length;i++){
			if(YLogScale){
				if(isAmp){
					if(dB) 	ret[i] = 20*Math.log10(ret[i]);
					else	ret[i] = 10*Math.log10(ret[i]);
				}
				else{
					ret[i] = 10*Math.log10(ret[i]);
				}
			}
		}
		return ret;
	}
	/**
	 * 周波数データを指定幅分積分して返す。（１Hzあたりのスペクトルを求めるのに使用する）
	 * サンプルデータはサンプル時刻を中心とするresolution幅の矩形とみなす。
	 * サンプルデータは０から始り、resolutionずつ増加する。
	 * 範囲外となるデータは０とみなす。
	 * @param in
	 * @param resolution
	 * @param band
	 * @return
	 */
	private double[]  CalcDensity(double[] in,double resolution,double band,boolean rt){
		int num = in.length;
		double[] ret = new double[num];
		double mod = band / resolution;
		if(rt) mod = Math.sqrt(mod);
		/// nnnは０、３，５，７ bandに（一部でも）含まれるサンプル数
		///  ０は１サンプル時間以内のband幅を示す、３は中心１サンプルと左右の２つのサンプルの一部分を示す。
		int nnn = (int)(Math.ceil(mod));
		if((nnn % 2)==0){
			nnn++;
		}
		double mmm = 0.0;
		if(mod > 1.0) mmm = 1.0 - ((nnn*resolution - band)/2.0);	// 余り部分のresolutionに対する割合(0..1)
		int nnl;	// 中心サンプルをのぞいて片方向にある対象サンプル数
		if(nnn == 0) nnl=0;
		else {
			nnl = (nnn-1)/2;
		}
		int loindex = -nnl;
		int hiindex = nnl;
		for(int i=0;i<num;i++,loindex++,hiindex++){
			double sum=0.0;
			if(mod>=1.0){ // 周波数解像度が１Hz以下なら積分する
				for(int j=loindex;j<=hiindex;j++){
					double add = 0.0;
					if(j>=0 && j<num){
						if((j==loindex || j==hiindex)){
							add = in[j]*mmm;
						}
						else {
							add = in[j];
						}
						
					}
					sum += add;
				}
				
			}
			else{// 周波数解像度が1Hzを超えていたら、１Hzあたりに換算する
				sum = in[i]*mod;
			}
			if(rt) sum /= 2.0;
			ret[i] = sum;
		}
		return ret;
	}
	public int getRefChannel(){
		return RefChannel;
	}
	public void setRefChannel(int ch){
		RefChannel = ch;
	}
	/**
	 * pngファイル名作成用
	 */
	private wintime DataTimeStart = new wintime();
	private wintime DataTimeEnd = new wintime();
	
	// ---------------------------------------------
	// すべてを書き直し
	public void repaintAll(){
		Rectangle r = new Rectangle(0,0,getWidth(),getHeight());
		repaint(r);
		parent.onSpectrumViewChange();
		DataTimeStart = parent.getDataTimeStart();
		DataTimeEnd  = parent.getDataTimeEnd();
	}

	// ---------------------------------------------
	// Y座標を描く
	private void drawYAxis(Graphics2D g,double[] d,double ymin,double ymax,Rectangle rall,Rectangle rgraph,String ytitle){
		g.setColor(ColorAxis);
		g.drawRect(rgraph.x,rgraph.y,rgraph.width-1,rgraph.height-1);
		float[] dasharray = {3.0f,3.0f};
		// 点線をあらわすストローク
		BasicStroke dashed2 = new BasicStroke(1.0f, BasicStroke.CAP_BUTT,BasicStroke.JOIN_MITER, 10.0f, dasharray, 0.0f);
		Stroke strokeorg = g.getStroke();
		g.setStroke(dashed2);
		//g.setColor(ColorAxisSub);

		String mes=new String(); 
		float[] dasharray3 = {1.0f,2.0f};
		BasicStroke dashed3 = new BasicStroke(1.0f, BasicStroke.CAP_BUTT,BasicStroke.JOIN_MITER, 10.0f, dasharray3, 0.0f);
		int divy=20;
		int yd = (rgraph.height-1)/divy;
		for(int i=0;i<divy+1;i++){
			int x1 = rgraph.x;
			int x2 = rgraph.x+ rgraph.width-1;
			int y1 = rgraph.y+i*(rgraph.height-1)/divy;
			if((i!=0)&&(i!=divy)){ 
				g.setColor(ColorAxis);
				g.drawLine(	x1,y1,	x2,	y1);
			}
			g.setColor(ColorAxisLabel);
			double label = ymax-(ymax-ymin)/divy*i;
			if(!Relative){
				label = Math.pow(10.0, label/10.0);
				mes = drawTools.prefixString(label,"%.1f");
			}
			else{
				if(YLogScale){
					mes = String.format("%6.2f",label);
				}
				else{
					mes = String.format("%.2e",label);
				}
			}
			drawTools.drawText(g,mes,	rgraph.x-2,rgraph.y+i*(rgraph.height-1)/divy,drawTools.POSITION_RIGHT_CENTER);
			if(i!=divy){
				g.setStroke(dashed3);
				for(int j=1;j<5;j++){
					g.setColor(ColorAxisSub);
					g.drawLine(	x1,y1+yd/(5)*j,x2,y1+yd/(5)*j);	
				}
				g.setStroke(dashed2);
			}
		}
		g.setColor(ColorAxisLabel);
		drawTools.drawText(g,ytitle,  rall.x+15, rall.y+rall.height/2, drawTools.POSITION_LEFT_CENTER,-2*3.14*90.0/360);
		
		g.setStroke(strokeorg);
	}
	
	// ---------------------------------------------
	//------------------------------------------------------
	// 
	
	//------------------------------------------------------
	// 
	private double roundFloor(double d){
		return Math.pow(10,Math.floor(Math.log10(d)));
	}
	//------------------------------------------------------
	// 
	private double roundCeil(double d){
		return Math.pow(10,Math.ceil(Math.log10(d)));
	}
	public void setXScalePeriod(boolean b){
		if(XScalePeriod != b){
			XScalePeriod = b;
			repaintAll();
		}
	}
	// 周波数軸の文字列を作る
	private String getXScaleString(double x){
		String mes;
		String fmt;
		if(XScalePeriod){ // 周期表示
			x = 1/x;
			fmt = "%6.3g";
			mes = String.format(fmt,x);
		}
		else{	// 周波数表示
			fmt = "%6.2g";
			mes = String.format(fmt,x);
		}
		return mes;
	}
	// ---------------------------------------------
	// X座標を描く
	private void drawXAxis(Graphics2D g,double [] f,double xmin,double xmax,Rectangle rall,Rectangle rgraph){
		g.setColor(ColorAxis);
		g.drawRect(rgraph.x,rgraph.y,rgraph.width-1,rgraph.height-1);
		// 点線をあらわすストローク
		float[] dasharray = {3.0f,3.0f};
		BasicStroke dashed2 = new BasicStroke(1.0f, BasicStroke.CAP_BUTT,BasicStroke.JOIN_MITER, 10.0f, dasharray, 0.0f);
		float[] dasharray3 = {1.0f,2.0f};
		BasicStroke dashed3 = new BasicStroke(1.0f, BasicStroke.CAP_BUTT,BasicStroke.JOIN_MITER, 10.0f, dasharray3, 0.0f);
		Stroke strokeorg = g.getStroke();
		g.setStroke(dashed2);
		//g.setColor(Color.gray);

		String mes=new String(); 
		// 対数スケールの場合
		if(XLogScale){
//			if(xmin<f[1]){
//				xmin = f[1];
//			}
//			if(xmax>f[f.length-1]){
//				xmax = f[f.length-1];
//			}
			//double min = roundFloor(xmin);
			if(xmin<1e-6) xmin=1e-6;
			double min = xmin;
			//double max = roundCeil(xmax);
			double max = xmax;
			int range = (int)(Math.floor(Math.log10(max/min)));
			double xtmp = min;
			for(int i=0;i<range;i++){
				if(xtmp > xmin && xtmp < xmax){
					int xa = transXLog(min,max,xtmp,rgraph.x,rgraph.x+rgraph.width-1);
					g.setColor(ColorAxis);
					g.drawLine(	xa,	rgraph.y, 	xa,	rgraph.y + rgraph.height-1);
					mes = getXScaleString(xtmp);
					g.setColor(ColorAxisLabel);
					drawTools.drawText(g,mes, xa,	rgraph.y + rgraph.height-1,drawTools.POSITION_CENTER_TOP);
				}
				for(int j=1;j<9;j++){
					double xtmp2 = xtmp * (j+1);
					//if(xtmp2>xmax) break;
					//if(xtmp2>xmin){
						int xa = transXLog(min,max,xtmp2,rgraph.x,rgraph.x+rgraph.width-1);
						g.setColor(ColorAxisSub);
						g.setStroke(dashed3);
						g.drawLine(	xa,	rgraph.y, 	xa,	rgraph.y + rgraph.height-1);
						g.setStroke(dashed2);
					//}
				}
				xtmp *= 10;
			}
			g.setColor(ColorAxisLabel);
			drawTools.drawText(g,getXScaleString(min), rgraph.x,	rgraph.y + rgraph.height-1,drawTools.POSITION_LEFT_TOP);
			drawTools.drawText(g,getXScaleString(max), rgraph.x+rgraph.width,	rgraph.y + rgraph.height-1,drawTools.POSITION_RIGHT_TOP);
		}
		// リニアスケールの場合
		else {
			int divx=10;
			for(int i=0;i<divx+1;i++){
				int xa;
				xa = rgraph.x+i*(rgraph.width-1)/divx;
				if((i!=0)&&(i!=divx)){ 
					g.setColor(ColorAxis);
					g.drawLine(	xa,	rgraph.y, 	xa,	rgraph.y + rgraph.height-1);
				}
				g.setColor(ColorAxisLabel);
				mes = getXScaleString(xmin+(xmax-xmin)/divx*i);
				if(i==0)			drawTools.drawText(g,mes, xa,	rgraph.y + rgraph.height-1,drawTools.POSITION_LEFT_TOP);
				else if(i==divx) 	drawTools.drawText(g,mes, xa,	rgraph.y + rgraph.height-1,drawTools.POSITION_RIGHT_TOP);
				else 				drawTools.drawText(g,mes, xa,	rgraph.y + rgraph.height-1,drawTools.POSITION_CENTER_TOP);
			}
		}
		g.setColor(ColorAxisLabel);
		String title;
		if(XScalePeriod){
			title = "[sec]";
		}
		else{
			title = "[Hz]";
		}
		drawTools.drawText(g,title,  rall.x+rall.width-10, rall.y+rall.height-50, drawTools.POSITION_RIGHT_BOTTOM);
		g.setStroke(strokeorg);
	}
	// ---------------------------------------------
	// 座標変換
	private int transXLog(double tdmin,double tdmax,double td,int xmin,int xmax){
		if(tdmin < 1e-8) tdmin = 1e-8;
		tdmin = Math.log10(tdmin);
		tdmax = Math.log10(tdmax);
		//if(td < 1e-100) td = 1e-100;  
		td = Math.log10(td);
		double d = (td - tdmin) / (tdmax-tdmin) * (double)(xmax - xmin);
		int ret = (int)(xmin+d);
		//if(ret>xmax) ret = xmax;
		return ret;
	}
	//------------------------------------------------------
	// 
	private int transXLiner(double tdmin,double tdmax,double td,int xmin,int xmax){
		double d = (td - tdmin) / (tdmax-tdmin) * (double)(xmax - xmin);
		int ret = (int)(xmin+d);
		//if(ret>xmax) ret = xmax;
		return ret;
	}
	/**
	 * Offset display channel を計算する
	 * @param d
	 * @param dispindex
	 * @return
	 */
	private double calcOffsetDisplay(double d,int dispindex){
		if(OffsetDisplay){ // Offset displayモードなら
			if(YLogScale) {	// Log Scaleなら
				d += dispindex * 10;
			}
			else{ // Linear Scale なら
			// サポートしない	
			}
		}
		return d;
	}
	// ---------------------------------------------
	// データを描く
	private void plot(Graphics2D g,double[] d,double[] f,double xmin,double xmax,double ymin,double ymax,Rectangle r,int channel,int dispindex){
		g.clipRect(r.x,r.y,r.width,r.height);
		int len = d.length;
		int[] dispDataX =  new int[len];
		int[] dispDataY =  new int[len];
		if(XLogScale){
			double ystep=(double)r.height / (ymax-ymin);
			
//			DispDataX = new int[len];
//			DispDataY = new int[len];
			for(int i=0;i<len;i++){
				dispDataX[i] = transXLog(xmin,xmax,f[i],r.x,r.x+r.width-1);
				if(dispDataX[i]<r.x) dispDataX[i] = r.x;
				double dy = d[i];
				if(d[i]==Double.NEGATIVE_INFINITY){
					dispDataY[i] = r.y+r.height;
				}
				else if(d[i]==Double.POSITIVE_INFINITY){
					dispDataY[i] = r.y;
				}
				else {
					dy = calcOffsetDisplay(dy,dispindex);
					dispDataY[i] = r.y+r.height-(int)(ystep*(dy-ymin));
				}
			}
			
			g.setColor(getDataColor(channel));
			g.drawPolyline(dispDataX,dispDataY,len);
		}
		else{
			//double xstep=(double)r.width /len;
	//		double xstep=(double)r.width /len;
			double ystep=(double)r.height / (ymax-ymin);
//			DispDataX = new int[len];
//			DispDataY = new int[len];
			for(int i=0;i<len;i++){
				//x[i] = r.x + (int)(xstep*i); 
				dispDataX[i] = transXLiner(xmin,xmax,f[i],r.x,r.x+r.width-1);
				double dy = d[i];
				if(d[i]==Double.NEGATIVE_INFINITY){
					dispDataY[i] = r.y+r.height;
				}
				else if(d[i]==Double.POSITIVE_INFINITY){
					dispDataY[i] = r.y;
				}
				else {
					dy = calcOffsetDisplay(dy,dispindex);
					dispDataY[i] = r.y+r.height-(int)(ystep*(dy-ymin));
				}
			}
			g.setColor(getDataColor(channel));
			g.drawPolyline(dispDataX,dispDataY,len);
		}
		if(channel == RefChannel){
			DispDataX = dispDataX;
			DispDataY = dispDataY;
		}
	}
	
	// ---------------------------------------------
	//------------------------------------------------------
	// つぎに使用可能なマーカーを見つける
	private int findNextUnusedMarker(){
		int ret;
		for(ret=1;ret<MarkerIndex.length-1;ret++){
			if(MarkerIndex[ret] < 0) return ret;
		}
		return -1;
	}
	//------------------------------------------------------
	// 近いマーカーを見つける
	private int findNearMarker(Point p){
		int ret;
		int area = 15*15;
		for(ret=1;ret<MarkerIndex.length;ret++){
			int marker = MarkerIndex[ret];
			if(marker >= 0){
				int x = DispDataX[marker];
				int y = DispDataY[marker];
				if((p.x-x)*(p.x-x)+(p.y-y)*(p.y-y) < area) return ret;
			}
		}
		return -1;
	}
	//------------------------------------------------------
	// マーカーを描画する
	private void drawMarker(Graphics g,int mx,int my,String mes,int mode){
		int [] x = new int[4];
		int [] y = new int[4];
		int size = 4;
		x[0] = mx - size;
		x[1] = mx;
		x[2] = mx + size;
		x[3] = mx;
		y[0] = my - size;
		y[1] = my - size*2;
		y[2] = my - size;
		y[3] = my ;
		if(mode == 0)	g.drawPolygon(x,y,x.length);
		else	g.fillPolygon(x,y,x.length);
		drawTools.drawText((Graphics2D)g,mes, x[1],	y[1],drawTools.POSITION_LEFT_BOTTOM);
	}
	
	// ---------------------------------------------
	// 近いデータポイントを見つける
	private int findNearPoint(int x,int y){
		int index;
		if(DispDataX[0]-5>x) return -1;
		if(DispDataX[DispDataX.length-1]+5<x) return -1;
		for(index=0;index<DispDataX.length-1;index++){
			if(DispDataX[index]<=x && x < DispDataX[index+1]){
				break;
			}
		}
		if(index>=DispDataX.length-1) return -1;
		return index;
	}
	// ---------------------------------------------
	// 近いデータポイントのうちピークを見つける
	private int findNearPeakPoint(int x,int y){
		int index=0;
		//int width = 20;
		int idx1 = findNearPoint(x-20,y);
		if(idx1<=-1) idx1 = 0;
		int idx2 = findNearPoint(x+20,y);
		if(idx2<=-1) idx2 = DispDataX.length-1;
		int ymax=0;
		for(int i=idx1;i<idx2;i++){
			if(i==idx1) {
				ymax = DispDataY[i]; 
				index = i;
			}
			else{
				if(ymax > DispDataY[i]){
					ymax = DispDataY[i];
					index=i;
				}
			}
		}
		return index;
	}
	// ---------------------------------------------
	// カーソルマーカーを描く
	private void setCursorMarker(Graphics g){
		if(MousePosition.x<0) return;
		if(MousePosition.y<0) return;
		if(!MouseActive) return;
		g.setColor(ColorCurMarker);
		if(MarkerAutoPeak){
			int index=findNearPeakPoint(MousePosition.x,MousePosition.y);
			if(index>=0) {
				MarkerIndex[0]=index;
				drawMarker(g,DispDataX[index],DispDataY[index],new String(" "),1);
			}
		}
		else{
			int index=findNearPoint(MousePosition.x,MousePosition.y);
			MarkerIndex[0]=index;
			if(index>=0) {
				drawMarker(g,DispDataX[index],DispDataY[index],new String(" "),1);
			}
		}
	}
	// ---------------------------------------------
	// マーカーを描く
	private void drawAllMarker(Graphics g){
		setCursorMarker(g);
		g.setColor(ColorMarker);
		for(int i=1;i<MARKER_MAX;i++){
			int index = MarkerIndex[i];
			if(index>0){
				String mes;
				if(i == MarkerIndex.length-1){
					mes = new String("R");
				}
				else{
					mes = String.format("%d",i);
				}
				drawMarker(g,DispDataX[index],DispDataY[index],mes,0);
			}
		}
	}
	
	// ---------------------------------------------
	// misc情報を描く
	private void drawMisc(Graphics g){
		if(IsPrint) return;
		Font fon2 = g.getFont().deriveFont(10.0f);
		g.setFont(fon2);
		g.setColor(ColorTextInfo);
		String mes2 = String.format("#%04d,draw=%dms,fft=%dms", FFTCalcCounter,(int)(PaintTimeEnd-PaintTimeStart),(int)(CalcTimeEnd-CalcTimeStart));
		int x = 10;
		int y = getSize().height - 5;
		drawTools.drawText((Graphics2D)g,mes2,x ,y,drawTools.POSITION_LEFT_BOTTOM);
	}
	// ---------------------------------------------
	// ch情報を描く
	private void drawChInfo(Graphics g,int[] ch){
		g.setClip(null);
		for(int i=0;i<ch.length;i++){
			int channel = ch[i];
			String mes = String.format("[%04x]", channel);
			int x = 110;
			int y = 20+i*20;
			int width = 50;
			int height = 20;
			if(OffsetDisplay){
				mes += String.format(" +%ddB", i*10);
				width += 70;
			}
			g.clearRect(x,y, width, height);
			g.setColor(getDataColor(channel));
			drawTools.drawText((Graphics2D)g,mes,x ,y,drawTools.POSITION_LEFT_TOP);
		}
	
	}
	/**
	 * マーカー表示文字列を作成する
	 * @return
	 */
	private String[] getMarkerStrings(){
		int deltamode = -1;
		if (MarkerIndex[MarkerIndex.length-1] >= 0){
			deltamode = MarkerIndex[MarkerIndex.length-1];
		}
		String[] mes = new String[MarkerIndex.length];
		BusyPaint=true;
		synchronized(DataSet){
			//System.out.println("getMarkerStrings");
			
			FFTChData cd = DataSet.get(RefChannel);
			if(cd==null){
				return null;
			}
			double[] fFTYaxis = cd.FFTYaxis;
			double[] fFTedFreq = cd.FFTedFreq;
				
			for(int i=0;i<MarkerIndex.length;i++){
				int marker=MarkerIndex[i];
				if(marker<0){
					continue;
				}
				double markerval = fFTYaxis[marker];
				double markertime = fFTedFreq[marker];
				double makervaldelta = 0;
				double markertimedelta = 0; 
				String freqStr ;
				String ampStr;
				if(deltamode != -1 && deltamode != marker){
					markertimedelta = markertime - fFTedFreq[deltamode];
					if(!Relative){
						makervaldelta = Math.pow(10.0, markerval/10.0)-Math.pow(10.0, fFTYaxis[deltamode]/10.0);
						ampStr = drawTools.prefixString(makervaldelta,"%4.2f");
					}
					else{
						makervaldelta = markerval - fFTYaxis[deltamode];
						if(YLogScale){
							ampStr = String.format("%5.2f\tdB" , makervaldelta);
						}
						else{
							ampStr = String.format("%.2e\t" , makervaldelta);
						}
					}
					freqStr = String.format("%5.2g\tHz",markertimedelta);
				}
				else{
					if(!Relative){
						markerval = Math.pow(10.0, markerval/10.0);
						ampStr = drawTools.prefixString(markerval,"%4.2f");
					}
					else{
						if(YLogScale){
							ampStr = String.format("%5.2f\tdB" , markerval);
						}
						else{
							ampStr = String.format("%.2e\t" , markerval);
						}
					}
					freqStr = String.format("%5.2g\tHz",markertime);
				}
				switch(i){
				case 0:// current cursor marker
					if(marker>=0){
						if(deltamode ==-1){
							mes[i] = String.format("[%04x]@:  \t%s    \t%s",RefChannel,ampStr,freqStr);
						}
						else {
							mes[i] = String.format("[%04x]delta@:  \t%s    \t%s",RefChannel,ampStr,freqStr);
						}
					}
					else {
						mes[i] = String.format("[%04x]@:  \t--- \t---",RefChannel);
					}
					break;
				case MARKER_MAX-1:	// reference marker
					if(marker>=0){
						mes[i] = String.format("[%04x]Ref:  \t%s    \t%s",RefChannel,ampStr,freqStr);
					}
					else {
						mes[i] = String.format("[%04x]Ref:  \t--- \t---",RefChannel);
					}
					break;
				default:
					if(marker>=0){
						if(deltamode==-1){
							mes[i] = String.format(" [%04x]%d:  \t%s    \t%s",RefChannel,i,ampStr,freqStr);
						}
						else {
							mes[i] = String.format(" [%04x]delta%d:  \t%s    \t%s",RefChannel,i,ampStr,freqStr);
						}
					}
					else {
						mes[i] = String.format(" [%04x]%d:  \t--- \t---",RefChannel,i);
					}
				}
			}
		}
		BusyPaint=false;
		return mes;
	}
	
	/**
	 * マーカーを描画する
	 * @param g
	 */
	private void drawMarkerInfo(Graphics g){
		g.setClip(null);
		//int count=0;
		
		int x = 250;
		int y = 20;
		int width = 250;
		int height = 20;
	
		String[] mes = getMarkerStrings();
		if(mes==null){
			return;
		}
		g.setColor(ColorTextInfo);
		int deltamode = -1;
		if (MarkerIndex[MarkerIndex.length-1] >= 0){
			deltamode = MarkerIndex[MarkerIndex.length-1];
		}
		if(deltamode>=0){
			g.clearRect(x,y, width, height);
			drawTools.drawText((Graphics2D)g,mes[MARKER_MAX-1],x ,y,drawTools.POSITION_LEFT_TOP);
			y+=height;
		}
		for(int i=0;i<MARKER_MAX-1;i++){
			if(MarkerIndex[i]>=0){
				g.clearRect(x,y, width, height);
				drawTools.drawText((Graphics2D)g,mes[i],x ,y,drawTools.POSITION_LEFT_TOP);
				y+=height;
			}
		}
		
	}
	// ---------------------------------------------
	private void paintComponetSub(Graphics graphic,Dimension dm){
		
		super.paintComponents(graphic);	
		
		if(BusyPaint){
			return;
		}
		BusyPaint = true;
		Graphics2D gd = (Graphics2D)graphic;
		gd.setBackground(ColorBackGround);
		gd.clearRect(0, 0, dm.width, dm.height);
		
//		if(FFTYaxis == null) return;
//		if(FFTYaxis.length < 8) return;
		
		PaintTimeStart = System.currentTimeMillis();
		Rall  =new Rectangle(0,0,dm.width,dm.height);
		Rgraph=new Rectangle(100,10,dm.width-100-10,dm.height-90);
//		String db;
//		if(SpectrumDensity) db = "[dBFs/Hz]";
//		else				db = "[dBFs]";
//		db = "[dBFs]";
		int[] channel = null;
		synchronized(DataSet){
			//System.out.println("paintComponetSub");
			FFTChData cd = DataSet.get(RefChannel);
			if(cd==null){
				return;
			}
			double[] fFTYaxis = cd.FFTYaxis;
			double[] fFTedFreq = cd.FFTedFreq;
			drawYAxis(gd,fFTYaxis,DisplayAmplitudeMin,DisplayAmplitudeMax,Rall,Rgraph,YAxisLabel);
			drawXAxis(gd,fFTedFreq,DisplayFreqMin,DisplayFreqMax,Rall,Rgraph);
			
			Set<Integer> set = DataSet.keySet();
			channel = new int[set.size()];
			
			Iterator<Integer> ite = set.iterator();
			int index=0;
			while(ite.hasNext()){
				int ch = ite.next();
				channel[index]=ch;
				FFTChData cd2 = DataSet.get(ch);
				double[] yaxis = cd2.FFTYaxis;
				double[] time =  cd2.FFTedFreq;
				if(yaxis != null && time !=null){
					plot(gd,yaxis,time,DisplayFreqMin,DisplayFreqMax,DisplayAmplitudeMin,DisplayAmplitudeMax,Rgraph,ch,index);
				}
				index++;
			}
		}
		BusyPaint = false;
		drawAllMarker(gd);
		drawMarkerInfo(gd);
		drawChInfo(gd,channel);
		drawMisc(gd);
		PaintTimeEnd = System.currentTimeMillis();
	}
	// ------------------------------------------
	// 描画の入り口
	private boolean BusyPaint=false;
	public void paintComponent(Graphics g){
		Dimension dm = this.getSize();
		paintComponetSub(g,dm);
	}
	// -------------------------------------------------------------------------------------------------
	// ----------------------------------
	// 印刷
	@Override
	public int print(Graphics g, PageFormat pf, int arg2)
			throws PrinterException {
		if(arg2 >= 1) return Printable.NO_SUCH_PAGE;
		
		Graphics2D g2 = (Graphics2D)g;
		g2.translate(pf.getImageableX(), pf.getImageableY());
		g2.scale(0.75,0.75);
		double hight = pf.getImageableHeight();
		double width = pf.getImageableWidth();
		Dimension dm = new Dimension();
		dm.setSize(width/0.75,hight/0.75);
		
		//CurrentDPI=72; 
		IsPrint=true;
		
		int cmorg = ColorMode;
		if(winchkgProperty.PrintColorAlwaysWhite){
			switch(ColorMode){
			case COLOR_BW_WHITE_BACK:
			case COLOR_BW_BLACK_BACK:
				setColorMode(COLOR_BW_WHITE_BACK);
				break;
			case COLOR_COLOR_BLACK_BACK:
			case COLOR_COLOR_WHITE_BACK:
				setColorMode(COLOR_COLOR_WHITE_BACK);
				break;
			}
		}
		paintComponetSub(g2,dm);
		setColorMode(cmorg);
		//CurrentDPI=96;
		IsPrint=false;
		return 0;
	}
	// -------------------------------------------------------------------------------------------------
	// マーカーを追加する
	private void addMarker(Point p,int mode){
		//int index  =  findNearPoint(p.x,p.y);
		int index = MarkerIndex[0];// current marker
		if(index<0) return ;
		int near;
		switch(mode){
		case 0:	// normal add
			// 近くにマーカーがあるかどうか
			near = findNearMarker(p);
			// なければ追加
			if(near<=0){
				int marker = findNextUnusedMarker();
				if(marker<=0) return;
				for(int j=1;j<MarkerIndex.length;j++){
					if(MarkerIndex[j] == index) return; // ダブりは禁止
				}
				MarkerIndex[marker] = index;
			}
			// あれば削除
			else{
				MarkerIndex[near] = -1;
			}
			break;
		case 1:	// add ref marker
			// 近くにマーカーがあるかどうか
			near = findNearMarker(p);
			if(near == MarkerIndex.length-1){
				MarkerIndex[MarkerIndex.length-1] = -1;
			}
			else MarkerIndex[MarkerIndex.length-1] = index;
			break;
		}
		repaintAll();
	}
	
	//------------------------------------
	private void OnMouseMove(){
		if(BusyPaint) return;
		repaintAll();
	}
	@Override
	public void mouseClicked(MouseEvent arg0) {
		int mod = arg0.getModifiers();
		int x = arg0.getX();
		int y = arg0.getY();
		// 右クリックのときはコンテキストメニュー
		if (SwingUtilities.isRightMouseButton(arg0)) {
			PopupMenuSpectrum.show(arg0.getComponent(), x, y);
		}
		// 左クリックはマーカーセット
		if (SwingUtilities.isLeftMouseButton(arg0)) {
			if((mod & MouseEvent.BUTTON1_MASK) != 0){
				if((mod & MouseEvent.CTRL_MASK) != 0){
					addMarker(arg0.getPoint(),1);
				}
				else{
					addMarker(arg0.getPoint(),0);
				}
			}
		}
	}
	@Override
	public void mouseEntered(MouseEvent arg0) {
		//  Auto-generated method stub
		MouseActive = true;
		
	}
	@Override
	public void mouseExited(MouseEvent arg0) {
		//  Auto-generated method stub
		MouseActive = false;
	}
	@Override
	public void mousePressed(MouseEvent arg0) {
		//  Auto-generated method stub
		
	}
	@Override
	public void mouseReleased(MouseEvent arg0) {
		//  Auto-generated method stub
		
	}
	@Override
	public void mouseDragged(MouseEvent e) {
		//  Auto-generated method stub
		
	}
	@Override
	public void mouseMoved(MouseEvent e) {
		MousePosition.x = e.getX();
		MousePosition.y = e.getY();
		int mod = e.getModifiers();
		if((mod & MouseEvent.SHIFT_MASK) != 0){
			MarkerAutoPeak=true;
		}
		else{
			MarkerAutoPeak=false;
		}
		OnMouseMove();
	}
	@Override
	public void dragEnter(DropTargetDragEvent dtde) {
		//  Auto-generated method stub
		
	}
	@Override
	public void dragExit(DropTargetEvent dte) {
		//  Auto-generated method stub
		
	}
	@Override
	public void dragOver(DropTargetDragEvent dtde) {
		//  Auto-generated method stub
		
	}
	@Override
	public void drop(DropTargetDropEvent dtde) {
		TreeSet<String> ts=DataBase.Dir2Files(dtde);
		if(ts==null) return;
		DataBase.addChData(ts);
	}
	@Override
	public void dropActionChanged(DropTargetDragEvent dtde) {
		//  Auto-generated method stub
		
	}
	public void setYLogScale(boolean selected) {
		YLogScale = selected;
		AutoAmpScale=true;
		doFFT();
		setAutoScale();		
	}
	public void onYSpanUp(){
		if(YLogScale){
			double step = getDisplayAmplitudeStep();
			if(step==1.0) step=2.0;
			else if(step==2.0) step = 5.0;
			else if(step==5.0) step = 10.0;
			else if(step==10.0) step = 20.0;
			else if(step==20.0) step = 50.0;
			setDisplayAmplitudeStep(step);
		}
		else{
			double range = getDisplayAmplitudeMax()-getDisplayAmplitudeMin();
			DisplayAmplitudeMin = getDisplayAmplitudeMin()-range;
			repaintAll();
		}
	}
	public void onYSpanDown(){
		if(YLogScale){
			double step = getDisplayAmplitudeStep();
			if(step==1.0) step=1.0;
			else if(step==2.0) step = 1.0;
			else if(step==5.0) step = 2.0;
			else if(step==10.0) step = 5.0;
			else if(step==20.0) step = 10.0;
			else if(step==50.0) step = 20.0;
			setDisplayAmplitudeStep(step);
		}
		else{
			double range = getDisplayAmplitudeMax()-getDisplayAmplitudeMin();
			range /= 2;
			DisplayAmplitudeMin = getDisplayAmplitudeMin()+range;
			repaintAll();
		}
	}
	public void onYLevelUp(){
		if(YLogScale){
			double step = getDisplayAmplitudeStep();
			setDisplayAmplitudeMax(getDisplayAmplitudeMax()+step);
		}
		else{
			double range = getDisplayAmplitudeMax()-getDisplayAmplitudeMin();
			range /= 10;
			DisplayAmplitudeMax = getDisplayAmplitudeMax()+range;
			DisplayAmplitudeMin = getDisplayAmplitudeMin()+range;
			repaintAll();
		}
	}
	public void onYLevelDown(){
		if(YLogScale){
			double step = getDisplayAmplitudeStep();
			setDisplayAmplitudeMax(getDisplayAmplitudeMax()-step);
		}
		else{
			double range = getDisplayAmplitudeMax()-getDisplayAmplitudeMin();
			range /= 10;
			DisplayAmplitudeMax = getDisplayAmplitudeMax()-range;
			DisplayAmplitudeMin = getDisplayAmplitudeMin()-range;
			repaintAll();
		}
	}
	/**
	 * 
	 * @param colorColorWhiteBack
	 */
	public void setColorMode(int colorColorWhiteBack) {
		ColorMode = colorColorWhiteBack;
		switch(colorColorWhiteBack){
		case COLOR_COLOR_WHITE_BACK:
			ColorBackGround = Color.white;
			ColorAxis = Color.black;
			ColorAxisSub = Color.gray;
			ColorAxisLabel = Color.gray;
			ColorTextInfo = Color.gray;
			ColorCurMarker = Color.orange;
			ColorMarker = Color.blue;
			break;
		case COLOR_COLOR_BLACK_BACK:
			ColorBackGround = Color.black;
			ColorAxis = Color.white;
			ColorAxisSub = Color.gray;
			ColorAxisLabel = Color.white;
			ColorTextInfo = Color.white;
			ColorCurMarker = Color.orange;
			ColorMarker = Color.yellow;
			break;
		case COLOR_BW_WHITE_BACK:
			ColorBackGround = Color.white;
			ColorAxis = Color.black;
			ColorAxisSub = Color.gray;
			ColorAxisLabel = Color.gray;
			ColorTextInfo = Color.gray;
			ColorCurMarker = Color.black;
			ColorMarker = Color.black;
			break;
		case COLOR_BW_BLACK_BACK:
			ColorBackGround = Color.black;
			ColorAxis = Color.white;
			ColorAxisSub = Color.gray;
			ColorAxisLabel = Color.white;
			ColorTextInfo = Color.white;
			ColorCurMarker = Color.white;
			ColorMarker = Color.white;
			break;
		}
	}
	private int ColorCalcMode=0;
	/**
	 * 
	 * @param colorAutoByChannel
	 */
	public void setColorCalcMode(int calcmode) {
		if(ColorCalcMode != calcmode){
			ColorCalcMode = calcmode;
			repaintAll();
		}
	}
	/**
	 * 
	 */
	public void clearDataAll() {
		synchronized(DataSet){
			//System.out.println("clearDataAll");
			DataSet.clear();
		}
	}
	/**
	 * 
	 * @param slidechannel
	 */
	public void setOffsetDisplay(boolean slidechannel){
		if(OffsetDisplay != slidechannel){
			OffsetDisplay =slidechannel;
			repaintAll();
		}
	}
	/**
	 * プロパティを読み書きする
	 * @param save
	 */
	public void LoadSaveProperty(boolean save) {
		if(save){
			winchkgProperty.SpectrumColor = getColorMode();
			winchkgProperty.SpectrumExportImageDir = ExportImageDir;
			
		}
		else{
			setColorMode(winchkgProperty.SpectrumColor);
			ExportImageDir = winchkgProperty.SpectrumExportImageDir;
		}

	}
	public int getColorMode() {
		return ColorMode;
	}
	
	
	private File chooseFile(){
		//------------------------------------------------------
		// save as ダイアログ
		JFileChooser filechooser = new JFileChooser(ExportImageDir);
		filechooser.setFileSelectionMode( JFileChooser.FILES_ONLY);	      
		FileNameExtensionFilter filter = new FileNameExtensionFilter("csv file", "csv");
		filechooser.addChoosableFileFilter(filter);
		filechooser.setDialogTitle("Save as");
		File tmp = new File(ExportImageDir + "Spctrum"+".csv");
		filechooser.setSelectedFile(tmp);
		//filechooser.ensureFileIsVisible(tmp);
		int selected = filechooser.showSaveDialog(null);
		File outfile=null;;
		if ( selected == JFileChooser.APPROVE_OPTION ) {
			tmp = filechooser.getSelectedFile();
			if(tmp.exists()){
				if(JOptionPane.NO_OPTION ==  JOptionPane.showConfirmDialog(null, "Over write ?","Confirmation",JOptionPane.YES_NO_OPTION)){
					return null;
				}
			}
			outfile = new File(tmp.getAbsolutePath());
		}
		else{
			return null;
		}
		return outfile;
		
	}
	/**
	 * CSVにエクスポートする。
	 */
	public void exportCSV() {
		// 計算して、
		if(!doFFT()){
			return ;
		}
		
		File ofile = chooseFile();
		if(ofile==null) return;
		try {
			FileWriter fw = new FileWriter(ofile);
			
			Set<Integer> set = DataSet.keySet();
			int dlen=0;
			Integer[] chary = set.toArray(new Integer[0]);
			if(chary!=null){
				String header = "Freq[Hz] ";
				for(int i=0;i<chary.length;i++){
					header += String.format(" , %04x [dB]", chary[i] );
				}
				fw.write(header+"\n");
							
				dlen = DataSet.get(chary[0]).FFTYaxis.length;
				for(int i=0;i<dlen;i++){
					String mes = null;
					for(int j=0;j<chary.length;j++){
						int ch = chary[j];
						FFTChData cd2 = DataSet.get(ch);
						if(j==0){
							double tm = cd2.FFTedFreq[i];
							mes = String.format("%f", tm);
						}	
						double dt = cd2.FFTYaxis[i];
						mes += String.format(" , " + "%f", dt);
					}
					fw.write(mes+"\n");
				}
				fw.flush();
			}
			fw.close();
			String status = String.format("Exported : %s \n %d channel(s) %d line(s)" , ofile.getAbsolutePath(), chary.length,dlen);
			JOptionPane.showConfirmDialog(this, status,"CSV Export",JOptionPane.DEFAULT_OPTION);
			
		} catch (IOException e) {
			return ;
		}
	}
}
